//
//  triple.h
//  cobalt
//
//  Created by Nicolas Godbout on 2013-08-28.
//  Copyright (c) 2013 Nicolas Godbout. All rights reserved.
//

#ifndef __cobalt__triple__
#define __cobalt__triple__

#include "node.h"

namespace rdf {
    namespace internal { struct TripleRecord; }
    
    class Triple {
    public:
        Triple();
        Triple(Triple const&) noexcept;
        Triple(Triple&&) noexcept;
        Triple& operator=(Triple const&) noexcept;
        Triple& operator=(Triple&&) noexcept;
        ~Triple() noexcept;
        
        RDFNode const& subject() const noexcept;
        RDFNode const& predicate() const noexcept;
        RDFNode const& object() const noexcept;
        
        explicit operator bool() const noexcept;
        bool operator<  (const Triple& other) const noexcept;
        bool operator== (const Triple& other) const noexcept;
        
    private:
        
        struct TripleRecord {
            uint32_t refCount;
            struct Flags {
                bool is_pattern         : 1;
                bool user_created       : 1;
                unsigned int padding    : 30;
            } flags;
            RDFNode     s, p, o;
            
            TripleRecord(RDFNode const& s, RDFNode const& p, RDFNode const& o)
            : s(s), p(p), o(o), refCount(1), flags({0,0}) {}
            
        };
        inline bool isPattern() const noexcept { return triple->flags.is_pattern; }
        inline void setPattern() noexcept { triple->flags.is_pattern = true; }
        

        struct lessSPO {
            bool operator() (Triple const& lhs, Triple const& rhs) const noexcept;
        };
        struct lessT {
            bool operator() (Triple const& lhs, Triple const& rhs) const noexcept {
                return lhs.triple < rhs.triple;
            }
        };
        struct lessST {
            bool operator() (Triple const& lhs, Triple const& rns) const noexcept;
        };
        struct lessPT {
            bool operator() (Triple const& lhs, Triple const& rns) const noexcept;
        };
        struct lessOT {
            bool operator() (Triple const& lhs, Triple const& rns) const noexcept;
        };
        
        // private constructor
        Triple(TripleRecord&&) noexcept;
        friend class Graph;
        friend class Environment;
        
        // members
        TripleRecord* triple;
    };
    
    class TripleFilter {
    public:
        virtual bool test(const Triple& triple) const = 0;
    };
    
    class TripleCallback {
    public:
        virtual void run(const Triple&, const class Graph&) = 0;
    };

    class TripleAction {
    public:
        TripleFilter        *test;
        TripleCallback      *action;
    public:
        virtual void run(const Triple& triple, class Graph& graph) = 0;
    };
    
    /** Private implementation **/

    /** Inline definitions **/
    using internal::TripleRecord;
    
    inline
    Triple::Triple()
    : triple(nullptr) {}
    
    inline
    Triple::Triple(TripleRecord&& rec) noexcept {
        triple = new TripleRecord(std::move(rec));
    }
    
    inline
    Triple::Triple(Triple const& other) noexcept {
        triple = other.triple;
        if ( triple )
            triple->refCount++;
    }
    
    inline
    Triple::Triple(Triple&& other) noexcept {
        triple = other.triple;
        other.triple = nullptr;
    }
    
    inline Triple&
    Triple::operator=(Triple const& other) noexcept {
        if ( triple && --triple->refCount == 0 )
            delete triple;
        triple = other.triple;
        if ( triple )
            triple->refCount++;
        return *this;
    }
    
    inline Triple&
    Triple::operator=(Triple&& other) noexcept {
        TripleRecord *tmp = triple;
        triple = other.triple;
        other.triple = tmp;
        return *this;
    }
    
    inline
    Triple::~Triple() noexcept {
        if ( triple && --triple->refCount == 0 )
            delete triple;
    }
    
    inline RDFNode const&
    Triple::subject() const noexcept {
        return triple->s;
    }
    inline RDFNode const&
    Triple::predicate() const noexcept {
        return triple->p;
    }
    inline RDFNode const&
    Triple::object() const noexcept {
        return triple->o;
    }
    
    inline
    Triple::operator bool() const noexcept {
        return (bool)triple;
    }
    
    inline bool
    Triple::operator< (const rdf::Triple &other) const noexcept {
        if ( !triple || !other.triple )
            return false;
        else if ( subject() < other.subject() )
            return true;
        else if ( other.subject() < subject() )
            return false;
        else if ( predicate() < other.predicate() )
            return true;
        else if ( other.predicate() < predicate() )
            return false;
        else
            return object() < other.object();
    }

    inline bool
    Triple::operator==(const rdf::Triple &other) const noexcept {
        if ( !triple || !other.triple )
            return true;
        else
            return     subject() == other.subject()
                    && predicate() == other.predicate()
                    && object() == other.object() ;
    }
    
} // namespace rdf

#endif /* defined(__cobalt__triple__) */
